オレオレ言語の字句解析 for いろんなプラットフォームについて


概要

Scalaでパーサコンビネータ作った以外で、ちゃんと言語の字句解析を行なったことがないのでやってみている。

参考

bison/flex(yacc/lex)について

http://hp.vector.co.jp/authors/VA022047/linux/bison.html


flex man

http://dinosaur.compilertools.net/flex/manpage.html



道具と使用場所に関する分析

やるべきことは

1.字句解析機を出力する

2.使う


なのだけれど、実際に使われる環境はどんななんだろうか。字句解析が行われるのが確定している環境は、

Unity

・ブラウザ


という感じで、ようはライブラリにできると良さそう。


適当にDocker上のLinuxから吐く。最終的にブラウザで使うならwasmにすればよかろう。c to wasmはどっかあった気がする。

そうでなくてもLLVM関連で出力できそう。

Unity向けであればC#ILか、やはりライブラリを出力したいところ。これにはどういう手段があるだろうか。



パースするべきものの設定とflexの実行

こんな感じの.lファイルをつくる。内容はmanに書いてあったやつ。


%option noyywrap

/* scanner for a toy Pascal-like language */


%{

/* need this for the call to atof() below */

#include <math.h>

%}


digit             [0-9]

alpha             [A-Za-z_]*

alpha_num         ({alpha}|{digit})

/*

こういうコメントは存在できるのか。

*/

%%


{digit}+ {

printf( "An integer: %s (%d)\n", yytext, atoi( yytext ) );

}


{digit}+"."{digit}* {

printf( "A float: %s (%g)\n", yytext, atof( yytext ) );

}


if|then|begin|end|procedure|function {

printf( "A keyword: %s\n", yytext );

}


{alpha} printf( "An identifier: %s\n", yytext );


"+"|"-"|"*"|"/" printf( "An operator: %s\n", yytext );


"{"[^}\n]*"}" /* eat up one-line comments */


[ \t\n]+ /* eat up whitespace */


. printf( "Unrecognized character: %s\n", yytext );


%%


main( argc, argv )

int argc;

char **argv;

{

++argv, --argc; /* skip over program name */

if ( argc > 0 ) {

yyin = fopen( argv[0], "r" );

} else {

yyin = stdin;

}


yylex();

}


うん、まだ全然わかんないや。とりあえずmanのいうとおりに進める。



flex parsel.l

で、lex.yy.cというcコードが出力される。

gcc lex.yy.c

それをgccとかでコンパイル。Macで実行するとclangが使われる。

特に名前を指定してないので、コンパイル結果としてa.outファイルが出力される。


ここでまあ、%option noyywrap とかを.lファイルの行頭に書いておくと、flexさんがyywrap(n)というdefineを追加してくれる。


今はまず字句解析の結果だけが欲しいので、これで動かす。



どんなものがパースされてどうなるんだろう

で、manに書いてあったサンプルがpascal-likeとのことだったので、雑にhello world + アルファを食わせてみる。


hello.pas

program Hello;


begin

  writeln('Hello, world.');

  Sentinel =0.0;

end.


で、

./a.out hello.pas


とか動かすと、

An identifier: program

An identifier: Hello

Unrecognized character: ;

A keyword: begin

An identifier: writeln

Unrecognized character: (

Unrecognized character: '

An identifier: Hello

Unrecognized character: ,

An identifier: world

Unrecognized character: .

Unrecognized character: '

Unrecognized character: )

Unrecognized character: ;

An identifier: Sentinel

Unrecognized character: =

A float: 0.0 (0)

Unrecognized character: ;

A keyword: end

Unrecognized character: .

、、、うん、まあ、なんていうか、うん。なるほど。

pascal likeだからな。


ちょっと改良して構文と内容の関連性がわかった。

で、やりたいことを実装に移す。



Emit2501形式では、時間経過的な駆動のために必要なパラメータと、実際にメソッドを着火するための機構が連結してある。

実行系もサクッと作ってしまったので、まあ、なんというか汚い。

これを綺麗にしていく第一歩として、パーサを実装していろんな環境で使えるようにしてみよう、という感じ。


とりあえずだいたいの部分はlexで字句解析できるようになった。


やったぜ!